package rtpsdp

import (
	"fmt"
	"log"

	"github.com/pion/ice/v2"
	"github.com/pion/sdp/v3"
	"gogs.smarteye.com/servere/gobvrtp/track"
	"gogs.smarteye.com/servere/gobvrtp/track_formats"
)

type SDPObject struct {
	object *sdp.SessionDescription
	tracks []track.Track
}

func ParseSDPObject(data []byte) (*SDPObject, error) {
	var object sdp.SessionDescription
	err := object.Unmarshal(data)
	if err != nil {
		return nil, err
	}

	c := &SDPObject{
		object: &object,
	}

	for _, m := range object.MediaDescriptions {
		if tk, err := c.newTrack(&object, m); err != nil {
			return nil, err
		} else {
			c.tracks = append(c.tracks, tk)
		}
	}

	return c, nil
}

func (c *SDPObject) GetTracks() []track.Track {
	return c.tracks
}

func (c *SDPObject) newTrack(object *sdp.SessionDescription, m *sdp.MediaDescription) (track.Track, error) {

	var fmtCtx = &track.FormatContext{
		MediaType: track.MediaType(m.MediaName.Media),
	}

	{
		fmtCtx.MediaInfo.MediaProto = m.MediaName.Protos
		if m.MediaTitle != nil {
			fmtCtx.MediaInfo.Information = m.MediaTitle.String()
		}
		fmtCtx.MediaInfo.KV = map[string]string{}
		for _, attribute := range m.Attributes {
			fmtCtx.MediaInfo.KV[attribute.Key] = attribute.Value
		}

		if v, ok := m.Attribute(sdp.AttrKeySSRC); ok {
			var err error
			fmtCtx.MediaInfo.SSRC, err = track.ParseSSRC(v)
			if err != nil {
				return nil, err
			}
		}
		if v, ok := m.Attribute(bvssrc); ok {
			fmtCtx.MediaInfo.BVSSRC, _ = track.ParseSSRC(v)
		}

		if v, ok := m.Attribute(rtpmap); ok {
			if value, err := track.ParseRTPMAP(v); err != nil {
				return nil, err
			} else {
				fmtCtx.MediaInfo.RtpMap = *value
			}
		} else {
			return nil, fmt.Errorf("no rtpmap")
		}

		if v, ok := m.Attribute(fmtp); ok {
			if value := track.ParseFmtp(v); value != nil {
				fmtCtx.MediaInfo.Fmtp = *value
			}
		}

		if _, ok := m.Attribute(sdp.AttrKeySendRecv); ok {
			fmtCtx.Direction = track.DirectionSendRecv
		}
		if _, ok := m.Attribute(sdp.AttrKeySendOnly); ok {
			fmtCtx.Direction = track.DirectionSendOnly
		}
		if _, ok := m.Attribute(sdp.AttrKeyRecvOnly); ok {
			fmtCtx.Direction = track.DirectionRecvOnly
		}

	}

	{
		if object.URI != nil {
			fmtCtx.SessionInfo.URI = object.URI.String()
		}
		if value, ok := object.Attribute(bvFileInfo); ok {
			fmtCtx.SessionInfo.JsonInfo = value
		}

		for _, td := range object.TimeDescriptions {
			fmtCtx.SessionInfo.StartTime = td.Timing.StartTime
			fmtCtx.SessionInfo.StopTime = td.Timing.StopTime
		}

		fmtCtx.SessionInfo.KV = map[string]string{}
		for _, attribute := range object.Attributes {
			fmtCtx.SessionInfo.KV[attribute.Key] = attribute.Value
		}
	}

	objectTrack, ok := track_formats.NewTrack(fmtCtx.GetRtpMap().GetCodec())
	if !ok {
		return nil, fmt.Errorf("track_formats.NewTrack fail")
	}

	if err := objectTrack.UnmarshalFormat(fmtCtx); err != nil {
		return nil, err
	}

	return objectTrack, nil
}

func (c *SDPObject) GetCandidates() []track.Candidate {
	object := c.object

	convertNetwork := func(t ice.NetworkType) track.NetworkType {
		switch t {
		case ice.NetworkTypeUDP4:
			return track.NetworkTypeUDP4
		case ice.NetworkTypeUDP6:
			return track.NetworkTypeUDP6
		case ice.NetworkTypeTCP4:
			return track.NetworkTypeTCP4
		case ice.NetworkTypeTCP6:
			return track.NetworkTypeTCP6
		default:
			return track.NetworkTypeUDP4
		}
	}

	var candidates []track.Candidate
	for _, m := range object.MediaDescriptions {
		if value, ok := m.Attribute(sdp.AttrKeyCandidate); ok {
			if candidate, err := ice.UnmarshalCandidate(value); err == nil {
				log.Println(candidate.NetworkType(), candidate.Address(), candidate.Port())
				candidates = append(candidates, track.Candidate{
					Type: convertNetwork(candidate.NetworkType()),
					Host: candidate.Address(),
					Port: candidate.Port(),
				})
			}
		}
	}

	return candidates
}
